package first

/*
	请实现一个函数用来判断字符串是否表示数值（包括整数和小数）。

	数值（按顺序）可以分成以下几个部分：

		1. 若干空格
		2. 一个小数或者整数
		3. （可选）一个'e'或'E'，后面跟着一个整数
		4. 若干空格
	小数（按顺序）可以分成以下几个部分：

		1. （可选）一个符号字符（'+' 或 '-'）
		2. 下述格式之一：
			1. 至少一位数字，后面跟着一个点 '.'
			2. 至少一位数字，后面跟着一个点 '.' ，后面再跟着至少一位数字
			3. 一个点 '.' ，后面跟着至少一位数字
		3. 整数（按顺序）可以分成以下几个部分：

			1. （可选）一个符号字符（'+' 或 '-'）
			2. 至少一位数字
	部分数值列举如下：

		- ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
	部分非数值列举如下：

		- ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]
	示例 1：

	输入：s = "0"
	输出：true
	示例 2：

	输入：s = "e"
	输出：false
	示例 3：

	输入：s = "."
	输出：false
	示例 4：

	输入：s = ".1"
	输出：true

	提示：

	1 <= s.length <= 20
	s 仅含英文字母（大写和小写），数字（0-9），加号 '+' ，减号 '-' ，空格 ' ' 或者点 '.' 。

	来源：力扣（LeetCode）
	链接：https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof
	著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
*/
func IsNumber(s string) bool {
	return isNumber(s)
}

/*
	数值形式如下 A[.[B]][e|EC] // 如果有整数部分、则B,C可以不存在
	.B[e|EC] // 如果没有整数部分、则小数部分不能为空
	其中:
	A代表整数部分
	B代表小数部分
	C代表指数部分
	+/-可以存在于A部分、也可以存在于C部分
	note:为了简化、可以先去掉字符串s的首尾空格
*/

func isNumber(s string) bool {
	for i := 0; i < len(s); i++ {
		if s[i] != ' ' {
			s = s[i:]
			break
		}
	}
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] != ' ' {
			s = s[:i+1]
			break
		}
	}
	if len(s) == 0 {
		return false
	}
	numeric := false
	numeric = scanInteger(&s)
	if len(s) == 0 {
		return numeric
	}
	if s[0] == '.' {
		s = s[1:]
		numeric = scanUnsignedInteger(&s) || numeric
	}
	if len(s) == 0 {
		return numeric
	}
	if s[0] == 'E' || s[0] == 'e' {
		s = s[1:]
		numeric = scanInteger(&s) && numeric
	}
	return numeric && len(s) == 0
}

func scanInteger(s *string) bool {

	if len(*s) > 0 && ((*s)[0] == '+' || (*s)[0] == '-') {
		*s = (*s)[1:]
	}
	return scanUnsignedInteger(s)
}

func scanUnsignedInteger(s *string) bool {
	before := 0
	var i int
	for i = 0; i < len(*s); i++ {
		if (*s)[i] < '0' || (*s)[i] > '9' {
			break
		}
	}
	if i == before {
		return false
	}
	(*s) = (*s)[i:]
	return true
}
